/**
 * PETALS - PETALS Services Platform.
 * Copyright (c) 2006 EBM Websourcing, http://www.ebmwebsourcing.com/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * -------------------------------------------------------------------------
 * $Id: MailBCExternalScheduler.java 154 27 sept. 06 ofabre $
 * -------------------------------------------------------------------------
 */

package org.ow2.petals.bc.mail.listeners;

import java.util.TimerTask;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.jbi.JBIException;
import javax.jbi.messaging.MessageExchange;
import javax.mail.Folder;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.Session;
import javax.mail.Store;
import javax.mail.Flags.Flag;

import org.apache.servicemix.JbiConstants;
import org.ow2.petals.bc.mail.Constants;
import org.ow2.petals.component.framework.util.Exchange;
import org.ow2.petals.component.framework.util.ExchangeImpl;

/**
 * This class is used to monitor a mailbox for new mails (accept() method). It
 * acts as a Listener. After new mail retrieval, the listener calls a
 * 'process(MimeMessage)' method on the MimeMessageManager, which acts as a
 * handler of MimeMessages.
 * 
 * @author ofabre - EBM Websourcing
 * 
 */
public class MailBCExternalScheduler extends TimerTask {

    protected Logger logger;

    private MailBCExternalListener externalListener;

    protected SessionDescriptor sessionDescriptor;

    protected MimeMessageManager processor;

    protected MailSessionManager mailSessionManager;

    /**
     * Instantiate a {@link MailBCExternalScheduler} for a specific address
     * 
     * @param externalListener
     *            the external listener for a given consumes
     * @param log
     *            the component logger
     * @param sessionDescriptor
     *            information about the mail session to open
     * @param mailSessionManager
     *            the {@link MailSessionManager}
     * @param processor
     *            the {@link MimeMessageManager}
     */
    public MailBCExternalScheduler(Logger log,
        MailBCExternalListener externalListener,
        SessionDescriptor sessionDescriptor,
        MailSessionManager mailSessionManager, MimeMessageManager processor) {
        super();
        this.logger = log;
        this.externalListener = externalListener;
        this.mailSessionManager = mailSessionManager;
        this.sessionDescriptor = sessionDescriptor;
        this.processor = processor;
    }

    /**
     * Create mail store and folder. Retrieve new mails and process them. Mail
     * store and folder are opened (connected) and closed each time the run
     * method is called. If the processing of a message fails, it is skipped and
     * the processing of other messages continue. All processed messages are
     * deleted from the folder.
     */
    public void run() {
        Message[] messages = null;
        Folder folder = null;
        Store store = null;
        try {
            // Create a Session
            Session session = mailSessionManager
                .createSessionPropertiesFromDescriptor(sessionDescriptor);

            // Get mail store and connect to it
            store = mailSessionManager.getStoreAndConnect(session,
                sessionDescriptor);

            // Get mail folder and open it
            folder = mailSessionManager.getFolderAndOpen(store,
                sessionDescriptor);

            // receive messages
            messages = accept(folder);

            // Process messages. If the processing of a message fails, it is
            // skipped and the processing of other messages continue
            for (Message message : messages) {
                process(message);
            }

            // Delete message from the mail folder, the purge is not supported
            // by POP3 protocol.
            // With POP3, the marked delete messages are deleted at the
            // disconnection.
            if (messages.length > 0
                && !Constants.MAIL_SCHEME_POP3.equals(sessionDescriptor
                    .getScheme())) {
                folder.expunge();
            }
        } catch (JBIException e) {
            logError("Error retrieving new mails", e);
        } catch (MessagingException e) {
            logWarning("Processed messages can't be deleted from the folder : "
                + folder, e);
        } finally {
            closeFolderAndStore(folder, store);
        }

    }

    /**
     * Search for new mails at linked address
     * 
     * @return the new Messages
     * @throws JBIException
     *             if new mail retrieval fails
     */
    protected Message[] accept(Folder folder) throws JBIException {
        return mailSessionManager.getNewMails(folder);
    }

    protected void logError(String msg, Throwable e) {
        if (logger != null)
            logger.log(Level.SEVERE, e.getMessage(), e);
        else
            e.printStackTrace();
    }

    protected void logWarning(String msg, Throwable e) {
        if (logger != null)
            logger.log(Level.WARNING, e.getMessage(), e);
        else
            e.printStackTrace();
    }

    protected void logInfo(String msg) {
        if (logger != null)
            logger.log(Level.INFO, msg);
    }

    /**
     * Call the processor's process() method. If the processing of a message
     * fails, it is skipped (just log error) and the processing of other
     * messages continue. In all cases (success or failure), the message is
     * deleted from the mail folder.
     * 
     * @param exchange
     */
    protected void process(Message message) {
        try {
        	System.out.println("using overridden mail component");
            this.logInfo("Message retrieved");
            MessageExchange exchange = processor.process(message);
            if (exchange != null) {
            	ExchangeImpl consumeExchange = (ExchangeImpl) externalListener.createConsumeExchange();
            	exchange.setEndpoint(consumeExchange.getEndpoint());
            	exchange.setService(consumeExchange.getService());
            	exchange.setInterfaceName(consumeExchange.getInterfaceName());
                externalListener.sendSync(new ExchangeImpl(exchange));
            }

            if (exchange.getError() != null) {
                this.logInfo("Impossible to send in PEtALS to "
                    + exchange.getEndpoint() + ": \n" + exchange.getError());
            } else if (exchange.getFault() != null) {
                this.logInfo("Impossible to send in PEtALS to "
                    + exchange.getEndpoint() + ": \n" + exchange.getFault());
            } else {
                this.logInfo("Message sent in PEtALS to "
                    + exchange.getEndpoint() + " - Operation = "
                    + exchange.getOperation() + " - Pattern = "
                    + exchange.getPattern());
            }
        } catch (JBIException e) {
            String msg = "Error processing a mail : "
                + message.getMessageNumber();
            logError(msg, e);
        } finally {
            try {
                message.setFlag(Flag.DELETED, true);
            } catch (MessagingException e) {
                logWarning("Mail message can't be marked as deleted. "
                    + "It will not be deleted from the store", e);
            }
        }
    }

    /**
     * Close previously opened folder and store.
     * 
     * @param folder
     *            the folder to close
     * @param store
     *            the store to close
     */
    protected void closeFolderAndStore(Folder folder, Store store) {
        try {
            if ((folder != null) && (folder.isOpen())) {
                folder.close(false);
            }
        } catch (Exception ex) {
            if (folder != null) {
                logWarning("Error closing mail Folder :" + folder.toString(),
                    ex);
            }
        }
        try {

            if ((store != null) && (store.isConnected())) {
                store.close();
            }
        } catch (Exception ex) {
            if (store != null) {
                logWarning("Error closing mail Store : " + store.toString(), ex);
            }
        }
    }
}
